
from data_recorder import DataRecorder
from play_sound import SoundPlayer
from target_cursor_manager import TargetCursorManager
from trial_manager import TrialManager

import tkinter as tk

import math
import datetime
import numpy as np


class CursorControl:
    """主控制器，协调各个模块工作"""

    def __init__(self, master, joystick_controller, pump_controller, decoder,settings_window):
        # 初始化主窗口
        self.master = master
        # self.master.attributes('-fullscreen', True)
        self.screen_w = master.winfo_screenwidth()
        self.screen_h = master.winfo_screenheight()

        # 创建画布
        self.canvas = tk.Canvas(
            master, width=self.screen_w, height=self.screen_h, bg='black'
        )
        self.canvas.pack(expand=True, fill=tk.BOTH)

        # 初始化子模块
        self.sound = SoundPlayer()  # 声音播放
        self.recorder = DataRecorder()  # 数据记录
        self.trial_manager = TrialManager(time_limit=20)  # 试验管理
        self.ui_manager = TargetCursorManager(  # 光标/目标管理
            self.canvas, self.screen_w, self.screen_h
        )
        self.settings = settings_window
        # 外部依赖
        self.joystick = joystick_controller
        self.joystick.run_in_thread()  # 启动摇杆线程
        self.decoder = decoder
        self.pump_controller = pump_controller


        # 状态变量
        self.text_id = None  # 提示文字ID
        self.update_interval = 20  # 光标更新间隔（毫秒）

        # 绑定事件
        self.master.protocol("WM_DELETE_WINDOW", self.on_close)
        self.master.bind("<Escape>", lambda e: self.on_close())
        # 初始化第一次试验
        self._init_first_trial()
        # 启动光标更新
        self.master.after(self.update_interval, self.update_cursor)


    def _init_first_trial(self):
        """初始化第一次试验的开始时间和目标位置"""
        # 生成第一个目标位置
        first_trial_num = self.trial_manager.next_trial()  # 从-1变为0
        target_idx = np.random.randint(0, 2)  # 随机第一个目标
        self.ui_manager.update_target_position(target_idx)
        # 记录第一次试验的开始时间
        self.start_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
        # print("_init_first_trial start_time = ", self.start_time, first_trial_num)

    def update_cursor(self):
        """更新光标位置（主循环）"""
        try:
            # 获取解码方向
            score_one = self.decoder.decoded_direction
            threshold = self.settings.get_threshold()
            # print(threshold)

            direction = 1 if score_one > threshold else 0
            # print(score_one, (score_one - threshold) * 20)
            # 计算光标速度（基于解码结果）
            joystick_pos = self.joystick.joystick_pos

            # velocity = [0, 8] if direction == 1 else [0, -8]
            velocity = [0, (score_one - threshold) * 20]
            # 根据摇杆位置决定是否移动光标
            # if 150 < joystick_pos[1] < 250:
            #     velocity = [0, 0]
            # 计算目标吸引力（吸引子机制）
            dx = self.ui_manager.target_pos[0] - self.ui_manager.cursor_pos[0]
            dy = self.ui_manager.target_pos[1] - self.ui_manager.cursor_pos[1]
            distance = math.hypot(dx, dy)
            unit_dx, unit_dy = (dx / distance, dy / distance) if distance != 0 else (0, 0)

            # 调整速度（结合吸引力）
            if velocity[0] != 0:
                sign_x = velocity[0] / abs(velocity[0])  # 正常获取符号
            else:
                sign_x = 0  # 若速度分量为零，定义符号为0
            velocity[0] = abs(velocity[0]) * (sign_x + unit_dx * 0)

            # 对 y 分量同理
            if velocity[1] != 0:
                sign_y = velocity[1] / abs(velocity[1])
            else:
                sign_y = 0
            velocity[1] = abs(velocity[1]) * (sign_y + unit_dy * 0.8)

            # 更新光标位置
            new_x = self.ui_manager.cursor_pos[0] + velocity[0]
            new_y = self.ui_manager.cursor_pos[1] + velocity[1]
            self.ui_manager.update_cursor_position(new_x, new_y)

            # 记录数据
            self.recorder.record_cursor_data(
                joystick_pos=self.joystick.joystick_pos,
                cursor_pos=self.ui_manager.cursor_pos,
                target_pos=self.ui_manager.target_pos,
                score=score_one,
                threshold=threshold
            )

            # 试验控制逻辑
            self._handle_trial_logic()
        except Exception as e:
            # 打印异常信息便于调试
            print(f"光标更新出错: {str(e)}")
            # 即使出错，仍尝试恢复循环
            self.master.after(self.update_interval, self.update_cursor)

    def _handle_trial_logic(self):
        """处理试验流程（成功/失败判断）"""
        # 检查超时
        if self.trial_manager.check_timeout():
            self._handle_failure()
            return

        # 检查成功
        # print(self.ui_manager.cursor_pos, self.ui_manager.target_pos)
        if self.trial_manager.check_success(self.ui_manager.cursor_pos, self.ui_manager.target_pos):
            self._handle_success()
            return

        # 继续更新光标
        self.master.after(self.update_interval, self.update_cursor)

    def _handle_success(self):
        """处理试验成功逻辑"""
        # 播放声音+控制水泵
        self.sound.play_success()
        self.pump_controller.open_pump()
        self.master.after(500, self.pump_controller.close_pump)

        # 记录试验结果
        trial_num = self.trial_manager.get_current_trial_num()
        # print("_handle_success, trial_num={}".format(trial_num))
        end_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
        self.recorder.record_trial_status(
            trial_num=trial_num,
            start_time=self.start_time,
            end_time=end_time,
            target_idx=self.ui_manager.current_target_idx,
            success=1
        )
        trial_num = self.trial_manager.get_current_trial_num()
        success_count = self.trial_manager.get_success_count()
        self.settings.update_trial(f"{success_count}/{trial_num+1}")

        # 切换到下一个试验
        self.master.after(2000, self._next_trial)

    def _handle_failure(self):
        """处理试验失败逻辑"""
        self.text_id = self.ui_manager.show_message("Failed!", "red")
        # 播放声音
        self.sound.play_failed()

        # 记录试验结果
        trial_num = self.trial_manager.get_current_trial_num()
        # print("_handle_failure, trial_num={}".format(trial_num))
        end_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
        self.recorder.record_trial_status(
            trial_num=trial_num,
            start_time=self.start_time,
            end_time=end_time,
            target_idx=self.ui_manager.current_target_idx,
            success=0
        )
        trial_num = self.trial_manager.get_current_trial_num()
        success_count = self.trial_manager.get_success_count()
        self.settings.update_trial(f"{success_count}/{trial_num + 1}")
        # 切换到下一个试验
        self.master.after(2000, self._next_trial)

    def _next_trial(self):
        """进入下一个试验（更新目标位置）"""
        # 清除提示文字
        self.text_id = self.ui_manager.clear_message(self.text_id)

        # 重置光标
        self.ui_manager.reset_cursor()

        # 生成下一个目标位置
        next_trial_num = self.trial_manager.next_trial()
        target_idx = np.random.randint(0, 2)  # 随机目标索引
        self.ui_manager.update_target_position(target_idx)

        # 记录试验开始时间
        self.start_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]

        # 继续更新光标
        self.master.after(self.update_interval, self.update_cursor)

    def on_close(self):
        print("Closing the window...")
        # 停止外部资源
        self.decoder.stop()
        # 保存数据
        self.recorder.save_all_data(self.joystick.joystick_data)
        if hasattr(self, 'joystick') and self.joystick:
            self.joystick.stop()
        # 销毁窗口
        self.master.destroy()

